﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
// test
using Silverlight.SqlStatementEvaluator;

namespace System.Data
{
    /// <summary>
    /// Provides DataSet functionality
    /// </summary>
    public class DataSet : EventArgs, INotifyPropertyChanged
    {
        #region Static Variables
        /// <summary>
        /// Defines "msdata" prefix
        /// </summary>
        private static XNamespace msdata = "urn:schemas-microsoft-com:xml-msdata";

        /// <summary>
        /// Defines "xs" prefix
        /// </summary>
        private static XNamespace xs = "http://www.w3.org/2001/XMLSchema";
        #endregion

        #region Data Fields
        /// <summary>
        /// "ComputerName" property data field
        /// </summary>
        private string _computerName;

        /// <summary>
        /// "ErrorMessage" property data field
        /// </summary>
        private string _errorMessage;

        /// <summary>
        /// "ErrorMessageDetails" property data field
        /// </summary>
        private string _errorMessageDetails;

        /// <summary>
        /// "HasErrors" property data field
        /// </summary>
        private bool _hasErrors;

        /// <summary>
        /// "Relations" property data field
        /// </summary>
        private DataRelationCollection _relations;

        /// <summary>
        /// Holds DataSet schema
        /// </summary>
        private XElement _schema;

        /// <summary>
        /// "Tables" property data field
        /// </summary>
        private DataTableCollection _tables;

        /// <summary>
        /// "TimeStamp" property data field
        /// </summary>
        private DateTime _timeStamp;

        /// <summary>
        /// "Views" proeprty data field
        /// </summary>
        private DataViewCollection _views;
        #endregion

        #region Events
        /// <summary>
        /// INotifyPropertyChanged interface implementation
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public DataSet()
        {
            this._timeStamp = DateTime.Now;
            this._tables = new DataTableCollection();
            this._tables.AnotherRowWasSelected += new EventHandler<DataSetEventArgs>(DataTableCollection_AnotherRowWasSelected);
            this._tables.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(DataTableCollection_PropertyChanged);
            this._tables.SchemaChanged += new EventHandler<DataSetEventArgs>(DataTableCollection_SchemaChanged);
            this.CreateSchema();
            this._views = new DataViewCollection();
            this._views.MasterDataRowCollectionRequest += new EventHandler<DataSetEventArgs>(Views_MasterDataRowCollectionRequest);
            this._relations = new DataRelationCollection();
           
        }

        /// <summary>
        /// Makes a new DataSet instance assigning name to it
        /// </summary>
        /// <param name="dataSetName">DataSet name</param>
        public DataSet(string dataSetName) : this()
        {
            // Validate argument
            if (string.IsNullOrEmpty(dataSetName))
            {
                throw new ArgumentNullException("dataSetName");
            }

            this.RenameSchema(dataSetName);
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets computer name
        /// </summary>
        public string ComputerName
        {
            get
            {
                return this._computerName;
            }
            set
            {
                this._computerName = value;
            }
        }

        /// <summary>
        /// Gets or sets DataSet name
        /// </summary>
        public string DataSetName
        {
            get
            {
                return this._schema.Attribute("id").Value;
            }
            set
            {
                // Validate argument
                if (string.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value");
                }

                this.RenameSchema(value);
            }
        }

        /// <summary>
        /// Gets or sets error message
        /// </summary>
        public string ErrorMessage
        {
            get
            {
                return this._errorMessage;
            }
            set
            {
                this._errorMessage = value;
                this.OnPropertyChanged("ErrorMessage");
            }
        }

        /// <summary>
        /// Gets or sets error message details
        /// </summary>
        public string ErrorMessageDetails
        {
            get
            {
                return this._errorMessageDetails;
            }
            set
            {
                this._errorMessageDetails = value;
                this.OnPropertyChanged("ErrorMessageDetails");
            }
        }

        /// <summary>
        /// Gets flag indicating whether DataTable has changes
        /// </summary>
        public bool HasChanges
        {
            get
            {
                for (int i = 0; i < this._tables.Count; i++)
                {
                    if (this._tables[i].HasChanges)
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        /// <summary>
        /// Gets or sets "HasErrors" flag value
        /// </summary>
        public bool HasErrors
        {
            get
            {
                return this._hasErrors;
            }
            set
            {
                this._hasErrors = value;
                this.OnPropertyChanged("HasErrors");
                this.OnPropertyChanged("HasNoErrors");
            }
        }

        /// <summary>
        /// Gets "HasNoErrors" flag value
        /// </summary>
        public bool HasNoErrors
        {
            get
            {
                return !this._hasErrors;
            }
        }

        /// <summary>
        /// Gets relations
        /// </summary>
        public DataRelationCollection Relations
        {
            get
            {
                return this._relations;
            }
        }

        /// <summary>
        /// Gets tables
        /// </summary>
        public DataTableCollection Tables
        {
            get
            {
                return this._tables;
            }
        }

        /// <summary>
        /// Gets time stamp
        /// </summary>
        public DateTime TimeStamp
        {
            get
            {
                return this._timeStamp;
            }
        }

        /// <summary>
        /// Gets views
        /// </summary>
        public DataViewCollection Views
        {
            get
            {
                return this._views;
            }
        }
        #endregion

        #region Indexers
        /// <summary>
        /// Gets DataTable or DataView with specified name
        /// </summary>
        /// <param name="sourceName">Source name</param>
        /// <returns>DataTable or DataView with specified name if found, otherwise null</returns>
        public IDataSource this[string sourceName]
        {
            get
            {
                // Try to find DataTable
                for (int i = 0; i < this._tables.Count; i++)
                {
                    if (this._tables[i].TableName.ToUpper(CultureInfo.InvariantCulture).Equals(sourceName.ToUpper(CultureInfo.InvariantCulture)))
                    {
                        return this._tables[i];
                    }
                }

                // Try to find DataView
                for (int i = 0; i < this._views.Count; i++)
                {
                    if (this._views[i].ViewName.ToUpper(CultureInfo.InvariantCulture).Equals(sourceName.ToUpper(CultureInfo.InvariantCulture)))
                    {
                        return this._views[i];
                    }
                }

                return null;
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Commits all the changes made since the last time AcceptChanges was called
        /// </summary>
        public void AcceptChanges()
        {
            for (int i = 0; i < this._tables.Count; i++)
            {
                this._tables[i].AcceptChanges();
            }
        }

        /// <summary>
        /// Adds data source to DataSet
        /// IDataSource interface implementation
        /// </summary>
        /// <param name="dataSource">Data source for adding</param>
        public void Add(IDataSource dataSource)
        {
            // Validate argument
            if (dataSource == null)
            {
                throw new ArgumentNullException("dataSource");
            }

            if (dataSource is DataTable)
            {
                this._tables.Add((DataTable)dataSource);
            }

            if (dataSource is DataView)
            {
                this._views.Add((DataView)dataSource);
            }
        }

        /// <summary>
        /// Copies DataSet structure
        /// </summary>
        /// <returns>DataSet with copied structure</returns>
        public DataSet Clone()
        {
            DataSet dataSet = new DataSet(this.DataSetName);
            for (int i = 0; i < this._tables.Count; i++)
            {
                dataSet._tables.Add(this._tables[i].Clone(false));
            }

            return dataSet;
        }

        /// <summary>
        /// Copies DataSet structure and data
        /// </summary>
        /// <returns>DataSet with copied structure and data</returns>
        public DataSet Copy()
        {
            DataSet dataSet = new DataSet(this.DataSetName);
            for (int i = 0; i < this._tables.Count; i++)
            {
                dataSet._tables.Add((DataTable)this._tables[i].Copy(false));
            }

            return dataSet;
        }

        /// <summary>
        /// Creates schema
        /// </summary>
        private void CreateSchema()
        {
            // Load schema
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("<xs:schema id=\"DataSet\" xmlns=\"\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">");
            stringBuilder.Append("<xs:element name=\"DataSet\" msdata:IsDataSet=\"true\" msdata:UseCurrentLocale=\"true\">");
            stringBuilder.Append("<xs:complexType>");
            stringBuilder.Append("<xs:choice minOccurs=\"0\" maxOccurs=\"unbounded\">");
            stringBuilder.Append("</xs:choice>");
            stringBuilder.Append("</xs:complexType>");
            stringBuilder.Append("</xs:element>");
            stringBuilder.Append("</xs:schema>");
            this._schema = XElement.Parse(stringBuilder.ToString());
        }

        /// <summary>
        /// Creates use case DataTable
        /// </summary>
        public static DataTable CreateUseCaseDataTable()
        {
            // Create DataTable
            DataTable useCaseDataTable = new DataTable("UseCase");

            DataColumn dataColumn = new DataColumn("UseCaseName", typeof(string));
            dataColumn.AllowDBNull = false;
            useCaseDataTable.Columns.Add(dataColumn);

            dataColumn = new DataColumn("UseCaseStateName", typeof(string));
            useCaseDataTable.Columns.Add(dataColumn);

            dataColumn = new DataColumn("UseCaseStepName", typeof(string));
            useCaseDataTable.Columns.Add(dataColumn);

            dataColumn = new DataColumn("RequestDateTime", typeof(DateTime));
            useCaseDataTable.Columns.Add(dataColumn);

            dataColumn = new DataColumn("RequestStatus", typeof(string));
            useCaseDataTable.Columns.Add(dataColumn);

            dataColumn = new DataColumn("RequestStatusDetails", typeof(string));
            useCaseDataTable.Columns.Add(dataColumn);

            dataColumn = new DataColumn("ComputerName", typeof(string));
            useCaseDataTable.Columns.Add(dataColumn);

            dataColumn = new DataColumn("UserName", typeof(string));
            useCaseDataTable.Columns.Add(dataColumn);

            return useCaseDataTable;
        }

        /// <summary>
        /// Deserializes data container from array of bytes
        /// </summary>
        /// <param name="data">Array of bytes</param>
        public void FromArray(byte[] data)
        {
            // Validate argument
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            // Transform byte array into string
            string xmlData = null;
            using (MemoryStream memoryStream = new MemoryStream(data))
            {
                using (BinaryReader binaryReader = new BinaryReader(memoryStream))
                {
                    xmlData = binaryReader.ReadString();
                }
            }

            this.FromXml(xmlData);
        }

        /// <summary>
        /// Creates DataSet from xml
        /// </summary>
        /// <param name="data">Xml data</param>
        public void FromXml(string data)
        {
            // Validate argument
            if (string.IsNullOrEmpty(data))
            {
                throw new ArgumentNullException("data");
            }

            // Load xml data
            XElement xElement = XElement.Parse(data);
            XElement cachedSchema = XElement.Parse(xElement.FirstNode.ToString());

            // Create and load DataSet
            this._schema = (XElement)xElement.FirstNode;

             

            // Determine if data is compressed
            bool compressed = false;

            XElement xmlData = (XElement) this._schema.NextNode;
            if (xmlData == null)
            {
                xElement.Add(XElement.Parse("<DataTable />"));
                xmlData = (XElement)this._schema.NextNode;
            }
            else if(xmlData.Name.LocalName == "NewDataSet")
            {
            }
            else
            {
                xmlData = XElement.Parse("<DataTable />");
                xmlData.Add(this._schema.NodesAfterSelf());
            }
            
            if (xmlData.Name.LocalName == "annotation")
            {
                xmlData = (XElement)xmlData.NextNode;
            }

            if (xmlData.Name.LocalName.StartsWith("_", StringComparison.Ordinal))
            {
                compressed = true;
            }

            IEnumerable<XElement> tableSchemas = GetTableSchemas();
            int i = 0;
            foreach (XElement tableSchema in tableSchemas)
            {
                DataTable dataTable = new DataTable(tableSchema);
                this.Tables.Add(dataTable);

                if (compressed)
                {
                    foreach (XElement row in DataSet.GetRows(xmlData, i))
                    {
                        DataRow dataRow = dataTable.NewRow();
                        foreach (XElement field in row.Elements())
                        {
                            int index = int.Parse(field.Name.LocalName.Substring(1), CultureInfo.InvariantCulture);
                            DataField dataField = dataRow.ItemArray[index];
                            dataField.UseCurrentCulture = false;
                            dataRow[DataSet.GetColumnName(tableSchema, index)] = field.Value;
                        }

                        for (int j = 0; j < dataRow.ItemArray.Count; j++)
                        {
                            dataRow.ItemArray[j].UseCurrentCulture = true;
                        }

                        dataTable.Rows.Add(dataRow);
                    }
                }
                else
                {
                    foreach (XElement row in DataSet.GetRows(xmlData, DataSet.GetTableName(tableSchema)))
                    {
                        DataRow dataRow = dataTable.NewRow();
                        foreach (XElement field in row.Elements())
                        {
                            DataField dataField = dataRow.ItemArray[field.Name.LocalName];
                            dataField.UseCurrentCulture = false;
                            dataRow[field.Name.LocalName] = field.Value;
                        }

                        for (int j = 0; j < dataRow.ItemArray.Count; j++)
                        {
                            dataRow.ItemArray[j].UseCurrentCulture = true;
                        }

                        dataTable.Rows.Add(dataRow);
                    }
                }

                i++;
            }

            // Create relations from primary / foreign keys
            IEnumerable<XElement> foreignKeysSchemas = this.GetForeignKeySchemas();
            foreach (XElement foreignKeySchema in foreignKeysSchemas)
            {
                // Create foreign key DataFieldCollection
                string childTableName = (foreignKeySchema.Element(xs + "selector").Attribute("xpath").Value).Substring(3);
                DataColumnCollection foreignKey = new DataColumnCollection();
                IEnumerable<XElement> foreignKeyFieldSchemas = DataSet.GetKeyFieldSchemas(foreignKeySchema);
                foreach (XElement foreignKeyFieldSchema in foreignKeyFieldSchemas)
                {
                    foreignKey.Add(this.Tables[childTableName].Columns[foreignKeyFieldSchema.Attribute("xpath").Value]);
                }

                // Create primary key DataFieldCollection
                DataColumnCollection primaryKey = new DataColumnCollection();
                XElement primaryKeySchema = this.GetPrimaryKeySchema(foreignKeySchema.Attribute("refer").Value);
                string parentTableName = (primaryKeySchema.Element(xs + "selector").Attribute("xpath").Value).Substring(3);
                IEnumerable<XElement> primaryKeyFieldSchemas = DataSet.GetKeyFieldSchemas(primaryKeySchema);
                foreach (XElement primaryKeyFieldSchema in primaryKeyFieldSchemas)
                {
                    primaryKey.Add(this.Tables[parentTableName].Columns[primaryKeyFieldSchema.Attribute("xpath").Value]);
                }
                
                // Create DataRelation
                DataRelation dataRelation = new DataRelation(foreignKeySchema.Attribute("name").Value, primaryKey, foreignKey);
                dataRelation.ParentTableName = parentTableName;
                dataRelation.ChildTableName = childTableName;
                this._relations.Add(dataRelation);
            }

            // Create relations from relationships
            IEnumerable<XElement> relationshipsSchemas = DataSet.GetRelationshipSchemas((XElement)xElement.FirstNode.NextNode);
            if (relationshipsSchemas != null)
            {
                foreach (XElement relationshipSchema in relationshipsSchemas)
                {
                    // Create foreign key DataFieldCollection
                    string childTableName = relationshipSchema.Attribute(msdata + "child").Value;
                    DataColumnCollection foreignKey = new DataColumnCollection();
                    string[] foreignKeyFieldNames = (relationshipSchema.Attribute(msdata + "childkey").Value).Split(' ');
                    for (int j = 0; j < foreignKeyFieldNames.Length; j++)
                    {
                        foreignKey.Add(this.Tables[childTableName].Columns[foreignKeyFieldNames[j]]);
                    }

                    // Create primary key DataFieldCollection
                    string parentTableName = relationshipSchema.Attribute(msdata + "parent").Value;
                    DataColumnCollection primaryKey = new DataColumnCollection();
                    string[] primaryKeyFieldNames = (relationshipSchema.Attribute(msdata + "parentkey").Value).Split(' ');
                    for (int j = 0; j < primaryKeyFieldNames.Length; j++)
                    {
                        primaryKey.Add(this.Tables[parentTableName].Columns[primaryKeyFieldNames[j]]);
                    }

                    // Create DataRelation
                    DataRelation dataRelation = new DataRelation(relationshipSchema.Attribute("name").Value, primaryKey, foreignKey);
                    dataRelation.ParentTableName = parentTableName;
                    dataRelation.ChildTableName = childTableName;
                    this._relations.Add(dataRelation);
                }
            }

           // xElement.RemoveNodes();
            //xElement.Add(cachedSchema);
            this._schema = cachedSchema;

            this.AcceptChanges();
            this.NormilizeDataSet();
        }


        /// <summary>
        /// Creates DataSet from xml
        /// </summary>
        /// <param name="data">Xml data</param>
        public void FromXml(XElement xElement)
        {
            // Validate argument
            if (xElement == null)
            {
                throw new ArgumentNullException("data");
            }

            // Load xml data
           // XElement xElement = XElement.Parse(data);
            XElement cachedSchema = XElement.Parse(xElement.FirstNode.ToString());

            // Create and load DataSet
            this._schema = (XElement)xElement.FirstNode;

            // Determine if data is compressed
            bool compressed = false;
            //XElement xmlData = XElement.Parse("<Datatable />");
            //xmlData.Add((XElement)this._schema.NextNode);
            XElement xmlData = (XElement)this._schema.NextNode;
            if (xmlData.Name.LocalName == "annotation")
            {
                xmlData = (XElement)xmlData.NextNode;
            }

            if (xmlData.Name.LocalName.StartsWith("_", StringComparison.Ordinal))
            {
                compressed = true;
            }

            IEnumerable<XElement> tableSchemas = GetTableSchemas();
            int i = 0;
            foreach (XElement tableSchema in tableSchemas)
            {
                DataTable dataTable = new DataTable(tableSchema);
                this.Tables.Add(dataTable);

                if (compressed)
                {
                    foreach (XElement row in DataSet.GetRows(xmlData, i))
                    {
                        DataRow dataRow = dataTable.NewRow();
                        foreach (XElement field in row.Elements())
                        {
                            int index = int.Parse(field.Name.LocalName.Substring(1), CultureInfo.InvariantCulture);
                            DataField dataField = dataRow.ItemArray[index];
                            dataField.UseCurrentCulture = false;
                            dataRow[DataSet.GetColumnName(tableSchema, index)] = field.Value;
                        }

                        for (int j = 0; j < dataRow.ItemArray.Count; j++)
                        {
                            dataRow.ItemArray[j].UseCurrentCulture = true;
                        }

                        dataTable.Rows.Add(dataRow);
                    }
                }
                else
                {
                    foreach (XElement row in DataSet.GetRows(xmlData, DataSet.GetTableName(tableSchema)))
                    {
                        DataRow dataRow = dataTable.NewRow();
                        foreach (XElement field in row.Elements())
                        {
                            DataField dataField = dataRow.ItemArray[field.Name.LocalName];
                            dataField.UseCurrentCulture = false;
                            dataRow[field.Name.LocalName] = field.Value;
                        }

                        for (int j = 0; j < dataRow.ItemArray.Count; j++)
                        {
                            dataRow.ItemArray[j].UseCurrentCulture = true;
                        }

                        dataTable.Rows.Add(dataRow);
                    }
                }

                i++;
            }

            // Create relations from primary / foreign keys
            IEnumerable<XElement> foreignKeysSchemas = this.GetForeignKeySchemas();
            foreach (XElement foreignKeySchema in foreignKeysSchemas)
            {
                // Create foreign key DataFieldCollection
                string childTableName = (foreignKeySchema.Element(xs + "selector").Attribute("xpath").Value).Substring(3);
                DataColumnCollection foreignKey = new DataColumnCollection();
                IEnumerable<XElement> foreignKeyFieldSchemas = DataSet.GetKeyFieldSchemas(foreignKeySchema);
                foreach (XElement foreignKeyFieldSchema in foreignKeyFieldSchemas)
                {
                    foreignKey.Add(this.Tables[childTableName].Columns[foreignKeyFieldSchema.Attribute("xpath").Value]);
                }

                // Create primary key DataFieldCollection
                DataColumnCollection primaryKey = new DataColumnCollection();
                XElement primaryKeySchema = this.GetPrimaryKeySchema(foreignKeySchema.Attribute("refer").Value);
                string parentTableName = (primaryKeySchema.Element(xs + "selector").Attribute("xpath").Value).Substring(3);
                IEnumerable<XElement> primaryKeyFieldSchemas = DataSet.GetKeyFieldSchemas(primaryKeySchema);
                foreach (XElement primaryKeyFieldSchema in primaryKeyFieldSchemas)
                {
                    primaryKey.Add(this.Tables[parentTableName].Columns[primaryKeyFieldSchema.Attribute("xpath").Value]);
                }

                // Create DataRelation
                DataRelation dataRelation = new DataRelation(foreignKeySchema.Attribute("name").Value, primaryKey, foreignKey);
                dataRelation.ParentTableName = parentTableName;
                dataRelation.ChildTableName = childTableName;
                this._relations.Add(dataRelation);
            }

            // Create relations from relationships
            IEnumerable<XElement> relationshipsSchemas = DataSet.GetRelationshipSchemas((XElement)xElement.FirstNode.NextNode);
            if (relationshipsSchemas != null)
            {
                foreach (XElement relationshipSchema in relationshipsSchemas)
                {
                    // Create foreign key DataFieldCollection
                    string childTableName = relationshipSchema.Attribute(msdata + "child").Value;
                    DataColumnCollection foreignKey = new DataColumnCollection();
                    string[] foreignKeyFieldNames = (relationshipSchema.Attribute(msdata + "childkey").Value).Split(' ');
                    for (int j = 0; j < foreignKeyFieldNames.Length; j++)
                    {
                        foreignKey.Add(this.Tables[childTableName].Columns[foreignKeyFieldNames[j]]);
                    }

                    // Create primary key DataFieldCollection
                    string parentTableName = relationshipSchema.Attribute(msdata + "parent").Value;
                    DataColumnCollection primaryKey = new DataColumnCollection();
                    string[] primaryKeyFieldNames = (relationshipSchema.Attribute(msdata + "parentkey").Value).Split(' ');
                    for (int j = 0; j < primaryKeyFieldNames.Length; j++)
                    {
                        primaryKey.Add(this.Tables[parentTableName].Columns[primaryKeyFieldNames[j]]);
                    }

                    // Create DataRelation
                    DataRelation dataRelation = new DataRelation(relationshipSchema.Attribute("name").Value, primaryKey, foreignKey);
                    dataRelation.ParentTableName = parentTableName;
                    dataRelation.ChildTableName = childTableName;
                    this._relations.Add(dataRelation);
                }
            }

            // xElement.RemoveNodes();
            //xElement.Add(cachedSchema);
            this._schema = cachedSchema;

            this.AcceptChanges();
            this.NormilizeDataSet();
        }




        /// <summary>
        /// Generates bindable objects
        /// </summary>
        /// <param name="connector">Connector instance</param>
        /// <returns>Collections of generated bindable objects</returns>
        public Dictionary<string, IList> GetBindableData(Connector connector)
        {
            Dictionary<string, IList> dictionary = new Dictionary<string, IList>();
            for (int i = 0; i < this._tables.Count; i++)
            {
                dictionary.Add(this._tables[i].TableName, this._tables[i].GetBindableData(connector));
            }

            return dictionary;
        }

        /// <summary>
        /// Gets column name base on column position in schema
        /// </summary>
        /// <param name="schema">Table schema</param>
        /// <param name="columnPosition">Column position</param>
        /// <returns>Column name</returns>
        private static string GetColumnName(XElement schema, int columnPosition)
        {
            IEnumerable<XElement> columns = from Element in schema.Descendants(xs + "element")
                                            select Element;

            int i = 0;
            foreach (XElement column in columns)
            {
                if (i == columnPosition)
                {
                    return column.Attribute("name").Value;
                }

                i++;
            }

            return null;
        }

        /// <summary>
        /// Gets DataSet without unchanged rows
        /// </summary>
        /// <returns>Copy of current DataSet without unchanged rows</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public DataSet GetChanges()
        {
            DataSet dataSet = this.Copy();
            for (int i = dataSet._tables.Count - 1; i >= 0; i--)
            {
                DataTable dataTable = dataSet._tables[i];

                // Process rows in original and copied tables
                for (int j = dataTable.Rows.Count - 1; j >= 0; j--)
                {
                    switch (dataTable.Rows[j].State)
                    {
                        case DataRowState.Unchanged:
                            // Delete unchanged row from original and copied tables
                            dataTable.Rows[j].Delete(true);
                            break;
                    }
                }
            }

            return dataSet;
        }

        /// <summary>
        /// Gets column schema
        /// </summary>
        /// <param name="tableSchema">Table schema</param>
        /// <param name="columnName">Column name</param>
        /// <returns>Column schema if found, otherwise null</returns>
        internal static XElement GetColumnSchema(XElement tableSchema, string columnName)
        {
            IEnumerable<XElement> columnSchemas = from Element in tableSchema.Descendants(xs + "element")
                   where Element.Attribute("name").Value == columnName
                   select Element;

            if (columnSchemas.Count() == 0)
            {
                return null;
            }

            return columnSchemas.Single();
        }

        /// <summary>
        /// Gets column schemas
        /// </summary>
        /// <param name="tableSchema">Table schema</param>
        /// <returns>Column schemas</returns>
        internal static IEnumerable<XElement> GetColumnSchemas(XElement tableSchema)
        {
            return from Element in tableSchema.Descendants(xs + "element")
                   select Element;
        }

        /// <summary>
        /// Prepares DataSet for submition
        /// </summary>
        /// <param name="getChanges">True for getting changes only</param>
        /// <returns>Copy of current DataSet with support tables</returns>
        private DataSet GetDataSetForSubmition(bool getChanges)
        {
            DataSet dataSet = null;
            if (getChanges)
            {
                dataSet = this.GetChanges();
            }
            else
            {
                dataSet = this.Copy();
            }

            for (int i = dataSet._tables.Count - 1; i >= 0; i--)
            {
                DataTable dataTable = dataSet._tables[i];

                // Add "RowIndex" and "RowState" columns
                DataColumn dataColumn = new DataColumn("RowIndex", Type.GetType("System.Int32"));
                dataTable.Columns.Add(dataColumn);
                dataColumn = new DataColumn("RowState", Type.GetType("System.String"));
                dataTable.Columns.Add(dataColumn);

                for (int j = dataTable.Rows.Count - 1; j >= 0; j--)
                {
                    DataRow dataRow = dataTable.Rows[j];
                    dataRow.Locked = true;

                    // Fill "RowIndex" and "RowState" fields
                    DataField dataField = dataRow.ItemArray["RowIndex"];
                    dataField.Value = dataRow.Index.ToString(CultureInfo.InvariantCulture);
                    dataField.AcceptChanges();
                    dataField = dataRow.ItemArray["RowState"];
                    dataField.Value = dataRow.State.ToString();
                    dataField.AcceptChanges();

                    // Call EndEdit on all rows
                    dataTable.Rows[j].EndEdit();
                }

                if (dataTable.Rows.Count > 0)
                {
                    // Make copy of DataTable
                    DataTable copiedDataTable = (DataTable)dataTable.Copy(true);

                    // Process rows in original and copied tables
                    for (int j = dataTable.Rows.Count - 1; j >= 0; j--)
                    {
                        switch (dataTable.Rows[j].State)
                        {
                            case DataRowState.Added:
                                // Delete unchanged row from original table
                                dataTable.Rows[j].Delete(true);
                                break;
                            case DataRowState.Deleted:
                                // Delete deleted row from copied table
                                copiedDataTable.Rows[j].Delete(true);
                                break;
                            case DataRowState.Modified:
                                // Reject changes for row, preserve it state for xml
                                DataRow dataRow = dataTable.Rows[j];
                                dataRow.RejectChanges();
                                DataField dataField = dataRow.ItemArray["RowState"];
                                dataField.Value = DataRowState.Modified.ToString();
                                dataField.AcceptChanges();
                                break;
                            case DataRowState.Unchanged:
                                // Delete unchanged row from copied table
                                copiedDataTable.Rows[j].Delete(true);
                                break;
                        }
                    }

                    // Add copied table to DataSet if it contains any record
                    if (copiedDataTable.Rows.Count > 0)
                    {
                        dataSet._tables.Add(copiedDataTable);
                    }
                }
                else
                {
                    if (getChanges)
                    {
                        dataSet._tables.Remove(dataTable);
                    }
                }
            }

            return dataSet;
        }

        /// <summary>
        /// Gets DataView for specified DataTable based on WHERE closure
        /// </summary>
        /// <param name="dataRelationList">Comma-separated list of DataRelation names</param>
        /// <param name="orderByClosure">ORDER BY closure</param>
        /// <param name="tableName">Table name</param>
        /// <param name="whereClosure">WHERE closure</param>
        /// <param name="values">WHERE parameters</param>
        /// <returns>DataView with DataRows from specified DataTable which satisfy WHERE closure</returns>
        public DataView GetDataView(string dataRelationList, string orderByClosure, string tableName, string whereClosure, params object[] parameters)
        {
            // Validate arguments
            if (string.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            DataTable dataTable = this._tables[tableName];
            return GetDataView(dataRelationList, orderByClosure, dataTable, whereClosure, parameters);
        }

        /// <summary>
        /// Gets DataView for specified DataTable based on WHERE closure
        /// </summary>
        /// <param name="dataRelationList">Comma-separated list of DataRelation names</param>
        /// <param name="orderByClosure">ORDER BY closure</param>
        /// <param name="dataTable">DataTable instance</param>
        /// <param name="whereClosure">WHERE closure</param>
        /// <param name="values">WHERE parameters</param>
        /// <returns>DataView with DataRows from specified DataTable which satisfy WHERE closure</returns>
        public DataView GetDataView(string dataRelationList, string orderByClosure, DataTable dataTable, string whereClosure, params object[] parameters)
        {
            // Return null if there is no specified DataTable in DataSet
            if (dataTable == null)
            {
                return null;
            }

            DataRelationCollection dataRelationCollection = new DataRelationCollection();

            // Populate DataRelationCollection according to dataRelationList
            if (!string.IsNullOrEmpty(dataRelationList))
            {
                string[] dataRelationNames = dataRelationList.Split(',');
                for (int i = 0; i < dataRelationNames.Length; i++)
                {
                    // Get DataRelations for specified name
                    IEnumerable<DataRelation> dataRelations = from Element in this._relations
                                                              where Element.ChildTableName == dataTable.TableName
                                                              && Element.RelationName == dataRelationNames[i]
                                                              select Element;

                    foreach (DataRelation dataRelation in dataRelations)
                    {
                        dataRelationCollection.Add(dataRelation);
                    }
                }
            }

            // Create DataView
            DataView dataView = new DataView(dataTable, dataRelationCollection);
            this._views.Add(dataView);
            dataView.From();
            dataView.Where(whereClosure, parameters);
            dataView.OrderBy(orderByClosure);
            return dataView;
        }

        /// <summary>
        /// Gets key field schemas
        /// </summary>
        /// <param name="keySchema">Key schema</param>
        /// <returns>Key field schemas</returns>
        private static IEnumerable<XElement> GetKeyFieldSchemas(XElement keySchema)
        {
            return keySchema.Elements(xs + "field");
        }

        /// <summary>
        /// Gets all foreign key schemas
        /// </summary>
        /// <returns>Foreign key shemas</returns>
        private IEnumerable<XElement> GetForeignKeySchemas()
        {
            // Validate required data
            if (this._schema == null)
            {
                throw new InvalidOperationException("Schema is missing");
            }

            return from Element in this._schema.Descendants(xs + "keyref")
                   select Element;
        }

        /// <summary>
        /// Gets primary key schema
        /// </summary>
        /// <returns>Primary key schema</returns>
        private XElement GetPrimaryKeySchema(string keyName)
        {
            // Validate required data
            if (this._schema == null)
            {
                throw new InvalidOperationException("Schema is missing");
            }

            return (from Element in this._schema.Descendants(xs + "unique")
                   where Element.Attribute("name").Value == keyName
                   select Element).Single();
        }

        /// <summary>
        /// Gets all relationship schemas
        /// </summary>
        /// <param name="annotation">Annotation node</param>
        /// <returns>Relationship shemas</returns>
        private static IEnumerable<XElement> GetRelationshipSchemas(XElement annotation)
        {
            string elementName = annotation.Name.LocalName;
            if (elementName == "annotation")
            {
                return from Element in annotation.Descendants(msdata + "Relationship")
                       select Element;
            }

            return null;
        }

        /// <summary>
        /// Gets all rows for specified table
        /// </summary>
        /// <param name="data">Table schema</param>
        /// <param name="tableName">Table name</param>
        /// <returns>All rows</returns>
        private static IEnumerable<XElement> GetRows(XElement data, string tableName)
        {
          IEnumerable<XElement> s = from Element in  data.Elements()
                                    where Element.Name == tableName
                   select Element;

          return s;
        }

        /// <summary>
        /// Gets all rows for specified table
        /// </summary>
        /// <param name="data">Table schema</param>
        /// <param name="tablePosition">Table position</param>
        /// <returns>All rows</returns>
        private static IEnumerable<XElement> GetRows(XElement data, int tablePosition)
        {
            return from Element in data.Elements()
                   where Element.Name == "_" + tablePosition.ToString(CultureInfo.InvariantCulture)
                   select Element;
        }

        /// <summary>
        /// Gets table name
        /// </summary>
        /// <param name="tableSchema">Selected table</param>
        /// <returns>Table name</returns>
        internal static string GetTableName(XElement tableSchema)
        {
            return tableSchema.Attribute("name").Value;
        }

        /// <summary>
        /// Gets table schema
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <returns>Table shema if found, otherwise null</returns>
        internal XElement GetTableSchema(string tableName)
        {
            // Validate argument
            if (string.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Validate required data
            if (this._schema == null)
            {
                throw new InvalidOperationException("Schema is missing");
            }

            IEnumerable<XElement> tableSchemas = from Element in this._schema.Descendants(xs + "element")
                                                 where Element.Attribute("type") == null
                                                 && Element.Attribute(msdata + "IsDataSet") == null
                                                 && Element.Attribute("name").Value == tableName
                                                 select Element;

            if (tableSchemas.Count() > 0)
            {
                return tableSchemas.Single();
            }

            return null;
        }

        /// <summary>
        /// Gets all table schemas
        /// </summary>
        /// <returns>Table shemas</returns>
        internal IEnumerable<XElement> GetTableSchemas()
        {
            // Validate required data
            if (this._schema == null)
            {
                throw new InvalidOperationException("Schema is missing");
            }

            return from Element in this._schema.Descendants(xs + "element")
                   where Element.Attribute("type") == null
                   && Element.Attribute(msdata + "IsDataSet") == null
                   select Element;
        }

        /// <summary>
        /// Gets value from specified table / row / column
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="columnName">Column name</param>
        /// <param name="value">Output value</param>
        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
        public void GetValue(string tableName, int rowIndex, string columnName, out bool? value)
        {
            // Validate arguments
            if (String.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Validate object existence
            DataTable dataTable = this._tables[tableName];
            if (dataTable == null)
            {
                throw new ArgumentOutOfRangeException("tableName");
            }

            dataTable.GetValue(rowIndex, columnName, out value);
        }

        /// <summary>
        /// Gets value from specified table / row / column
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="columnName">Column name</param>
        /// <param name="value">Output value</param>
        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
        public void GetValue(string tableName, int rowIndex, string columnName, out DateTime? value)
        {
            // Validate arguments
            if (String.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Validate object existence
            DataTable dataTable = this._tables[tableName];
            if (dataTable == null)
            {
                throw new ArgumentOutOfRangeException("tableName");
            }

            dataTable.GetValue(rowIndex, columnName, out value);
        }

        /// <summary>
        /// Gets value from specified table / row / column
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="columnName">Column name</param>
        /// <param name="value">Output value</param>
        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
        public void GetValue(string tableName, int rowIndex, string columnName, out double? value)
        {
            // Validate arguments
            if (String.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Validate object existence
            DataTable dataTable = this._tables[tableName];
            if (dataTable == null)
            {
                throw new ArgumentOutOfRangeException("tableName");
            }

            dataTable.GetValue(rowIndex, columnName, out value);
        }

        /// <summary>
        /// Gets value from specified table / row / column
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="columnName">Column name</param>
        /// <param name="value">Output value</param>
        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
        public void GetValue(string tableName, int rowIndex, string columnName, out int? value)
        {
            // Validate arguments
            if (String.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Validate object existence
            DataTable dataTable = this._tables[tableName];
            if (dataTable == null)
            {
                throw new ArgumentOutOfRangeException("tableName");
            }

            dataTable.GetValue(rowIndex, columnName, out value);
        }

        /// <summary>
        /// Gets value from specified table / row / column
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="columnName">Column name</param>
        /// <param name="value">Output value</param>
        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
        public void GetValue(string tableName, int rowIndex, string columnName, out long? value)
        {
            // Validate arguments
            if (String.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Validate object existence
            DataTable dataTable = this._tables[tableName];
            if (dataTable == null)
            {
                throw new ArgumentOutOfRangeException("tableName");
            }

            dataTable.GetValue(rowIndex, columnName, out value);
        }

        /// <summary>
        /// Gets value from specified table / row / column
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="columnName">Column name</param>
        /// <param name="value">Output value</param>
        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
        public void GetValue(string tableName, int rowIndex, string columnName, out string value)
        {
            // Validate arguments
            if (String.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Validate object existence
            DataTable dataTable = this._tables[tableName];
            if (dataTable == null)
            {
                throw new ArgumentOutOfRangeException("tableName");
            }

            dataTable.GetValue(rowIndex, columnName, out value);
        }

        /// <summary>
        /// Normilizes DataSet after restoration from xml
        /// </summary>
        private void NormilizeDataSet()
        {
            for (int i = 0; i < this._tables.Count; i++)
            {
                // Skip original table
                if (!this._tables[i].TableName.StartsWith("CopyOf", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                // Get pair of original and copied tables
                DataTable copiedDataTable = this._tables[i];
                DataTable dataTable = this._tables[copiedDataTable.TableName.Substring(6)];

                // Process all rows in copied table
                for (int j = 0; j < copiedDataTable.Rows.Count; j++)
                {
                    // Get copied row
                    DataRow copiedDataRow = copiedDataTable.Rows[j];

                    // Get new row from original table
                    DataRow dataRow = dataTable.NewRow();

                    // Copy modified fields into original row
                    for (int m = 0; m < dataRow.ItemArray.Count; m++)
                    {
                        dataRow[m] = copiedDataRow[m];
                        dataRow.ItemArray[m].AcceptChanges();
                    }

                    dataTable.Rows.Add(dataRow);
                }
            }

            // Remove copied tables
            for (int i = this._tables.Count - 1; i >= 0; i--)
            {
                if (this._tables[i].TableName.StartsWith("CopyOf", StringComparison.OrdinalIgnoreCase))
                {
                    this._tables.Remove(this._tables[i]);
                }
            }
        }

        /// <summary>
        /// Raises PropertChanged event
        /// </summary>
        /// <param name="propertyName">Property name</param>
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Recalculates DataViews using DataRelations
        /// </summary>
        /// <param name="parentTableName">Parent table name</param>
        private void RecalculateDataViews(string parentTableName)
        {
            // Validate argument
            if (string.IsNullOrEmpty(parentTableName))
            {
                throw new ArgumentNullException("parentTableName");
            }

            // Get DataRelations for parent DataTable
            IEnumerable<DataRelation> dataRelations = from Element in this._relations
                                                      where Element.ParentTableName == parentTableName
                                                      select Element;

            foreach (DataRelation dataRelation in dataRelations)
            {
                // Get DataViews for child DataTable
                IEnumerable<DataView> dataViews = from Element in this._views
                                                  where Element.Table.TableName == dataRelation.ChildTableName
                                                  select Element;

                foreach (DataView dataView in dataViews)
                {
                    dataView.From();
                    dataView.Where();
                    dataView.OrderBy();
                    dataView.GetBindableData();
                }
            }
        }

        /// <summary>
        /// Recalculates errors
        /// </summary>
        private void RecalculateErrors()
        {
            // Prepare result variable
            bool hasErrors = false;

            // Check all tables
            for (int i = 0; i < this._tables.Count; i++)
            {
                if (this._tables[i].HasErrors)
                {
                    hasErrors = true;
                }
            }

            // Change "HasErrors" property if needed
            if (this._hasErrors != hasErrors)
            {
                this._hasErrors = hasErrors;
                this.OnPropertyChanged("HasErrors");
                this.OnPropertyChanged("HasNoErrors");
            }
        }

        /// <summary>
        /// Rejects all changes made since AcceptChanges was last called
        /// </summary>
        public void RejectChanges()
        {
            for (int i = 0; i < this._tables.Count; i++)
            {
                this._tables[i].RejectChanges();
            }
        }

        /// <summary>
        /// Removes data source from DataSet
        /// </summary>
        /// <param name="dataSource">DataSource for removing</param>
        public void Remove(IDataSource dataSource)
        {
            // Validate argument
            if (dataSource == null)
            {
                throw new ArgumentNullException("dataSource");
            }

            if (dataSource is DataTable)
            {
                this._tables.Remove((DataTable)dataSource);
            }

            if (dataSource is DataView)
            {
                this._views.Remove((DataView)dataSource);
            }
        }

        /// <summary>
        /// Renames schema
        /// </summary>
        /// <param name="dataSetName">DataSet name</param>
        private void RenameSchema(string dataSetName)
        {
            this._schema.Attribute("id").Value = dataSetName;
            this._schema.Element(xs + "element").Attribute("name").Value = dataSetName;
        }

        /// <summary>
        /// Sets value into specified table / row / filed
        /// Creates row if table has no rows or row index equals row count
        /// Throws an exception if table has rows but row index frate than row count
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="columnIndex">Column index</param>
        /// <param name="value">Value for assigning</param>
        internal void SetValue(string tableName, int rowIndex, int columnIndex, object value)
        {
            // Validate arguments
            if (String.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Get DataTable or create one if it doesn't exist
            DataTable dataTable = this._tables[tableName];
            if (dataTable == null)
            {
                throw new ArgumentOutOfRangeException("tableName");
            }

            dataTable.SetValue(rowIndex, columnIndex, value);
        }

        /// <summary>
        /// Sets value into specified table / row / filed
        /// Creates table if it doesn't exist
        /// Creates row if table has no rows or row index equals row count
        /// Throws an exception if table has rows but row index frate than row count
        /// Creates column if it doesn't exist
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="columnName">Column name</param>
        /// <param name="value">Value for assigning</param>
        /// <param name="valueType">Value type</param>
        public void SetValue(string tableName, int rowIndex, string columnName, object value)
        {
            // Validate arguments
            if (String.IsNullOrEmpty(tableName))
            {
                throw new ArgumentNullException("tableName");
            }

            // Get DataTable or create one if it doesn't exist
            DataTable dataTable = this._tables[tableName];
            if (dataTable == null)
            {
                dataTable = new DataTable(tableName);
                this._tables.Add(dataTable);
            }

            dataTable.SetValue(rowIndex, columnName, value);
        }

        /// <summary>
        /// Sets value into specified table / row
        /// </summary>
        /// <param name="tableName">Table name</param>
        /// <param name="rowIndex">Row index</param>
        /// <param name="values">Array of values</param>
        public void SetValues(string tableName, int rowIndex, object[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                this.SetValue(tableName, rowIndex, i, values[i]);
            }
        }

        /// <summary>
        /// Serializes DataSet into array of bytes
        /// </summary>
        /// <param name="getChanges">True for getting changes only</param>
        /// <returns>Array of bytes</returns>
        public byte[] ToArray(bool getChanges)
        {
            byte[] data = null;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream))
                {
                    binaryWriter.Write(ToXml(getChanges));
                    data = memoryStream.ToArray();
                }
            }

            return data;
        }

        /// <summary>
        /// Creates xml representation of DataSet with xml compressing
        /// </summary>
        /// <param name="getChanges">True for getting changes only</param>
        /// <returns>Xml data with xsd schema</returns>
        public string ToXml(bool getChanges)
        {
            return this.ToXml(getChanges, false);
        }

        /// <summary>
        /// Creates xml representation of DataSet
        /// </summary>
        /// <param name="getChanges">True for getting changes only</param>
        /// <param name="compress">True for xml compressing</param>
        /// <returns>Xml data with xsd schema</returns>
        public string ToXml(bool getChanges, bool compress)
        {
            DataSet dataSet = this.GetDataSetForSubmition(getChanges);
            XElement dataSetXml = new XElement(this.DataSetName);
            dataSetXml.Add(dataSet._schema);
            for (int i = 0; i < dataSet._tables.Count; i++)
            {
                for (int j = 0; j < dataSet._tables[i].Rows.Count; j++)
                {
                    DataRow dataRow = dataSet._tables[i].Rows[j];
                    dataSetXml.Add(dataRow.GetXmlData(compress, i));
                }
            }
          

            if (compress)
            {
                dataSetXml.Name = "_" + dataSetXml.Name.LocalName;
            }

            return dataSetXml.ToString();
        }
        #endregion

        #region Event Handlers
        /// <summary>
        /// Handles DataTableCollection.AnotherRowWasSelected event
        /// </summary>
        /// <param name="sender">Event source</param>
        /// <param name="e">Event arguments</param>
        void DataTableCollection_AnotherRowWasSelected(object sender, DataSetEventArgs e)
        {
            this.RecalculateDataViews(e.DataTableName);
        }

        /// <summary>
        /// Handles DataRowCollection.PropertyChanged event
        /// </summary>
        /// <param name="sender">Event source</param>
        /// <param name="e">Event arguments</param>
        void DataTableCollection_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Contains("HasError"))
            {
                this.RecalculateErrors();
            }
            else
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, e);
                }
            }
        }

        /// <summary>
        /// Handles Tables.SchemaChanged event
        /// </summary>
        /// <param name="sender">Event source</param>
        /// <param name="e">Event arguments</param>
        void DataTableCollection_SchemaChanged(object sender, DataSetEventArgs e)
        {
            DataTable dataTable = (DataTable)sender;
            switch (e.Command)
            {
                case "Add":
                    // Add column schema to table schema
                    XElement tableSchema = this.GetTableSchema(dataTable.TableName);
                    if (tableSchema == null)
                    {
                        this._schema.Element(xs + "element").Element(xs + "complexType").Element(xs + "choice").Add(dataTable.Schema);
                    }
                    break;
                case "Remove":
                    if (dataTable.Schema.Parent == null)
                    {
                        string dataTableName = DataSet.GetTableName(dataTable.Schema);
                        XElement dataTableSchema = this.GetTableSchema(dataTableName);
                        dataTableSchema.Remove();
                    }
                    else
                    {
                        dataTable.Schema.Remove();
                    }

                    break;
            }
        }

        /// <summary>
        /// Handles Views.MasterDataRowCollectionRequest event
        /// </summary>
        /// <param name="sender">Event source</param>
        /// <param name="e">Event arguments</param>
        void Views_MasterDataRowCollectionRequest(object sender, DataSetEventArgs e)
        {
            e.DataRowCollection = this.Tables[e.Relation.ParentTableName].Rows.SelectedRows;
        }
        #endregion

        public void Dispose()
        { 

        }

        public void Clear()
        {
        }
    }
}
